<!doctype HTML public "-//W3C//DTD HTML 4.0 Frameset//EN">
<html>
<title>Bloomberg Development Environment</title>
<html>
<pre>
// Copyright 2014-2023 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// bmqt_correlationid.h                                               -*-C++-*-
#ifndef INCLUDED_BMQT_CORRELATIONID
#define INCLUDED_BMQT_CORRELATIONID

//@PURPOSE: Provide a value-semantic type usable as an efficient identifier.
//
//@CLASSES:
//  bmqt::CorrelationId:     correlation ID class.
//  bmqt::CorrelationIdLess: comparison functor for &#39;CorrelationId&#39; as map key
//  bsl::hash:               hash functor specialization
//
//@DESCRIPTION: This component implements a value-semantic class,
// &#39;bmqt::CorrelationId&#39;, which can be used to identify any async operations.
// The correlationId contains a value (64-bit integer, raw pointer or
// sharedPtr) supplied by the application or uses an auto-assigned value.  The
// type and the value of the correlationId can be set at construction time and
// changed later via the &#39;setNumeric()&#39;, &#39;setPointer()&#39; and &#39;setSharedPointer&#39;
// methods.  Alternatively, a &#39;CorrelationId::AutoValue&#39; can be used to
// generate a unique correlationId from within the process.  The
// &#39;bmqt::CorrelationIdLess&#39; comparison functor can be used for storing
// &#39;CorrelationId&#39; in a map as the key element; and a hash functor
// specialization is provided in the &#39;bsl::hash&#39; namespace.
//
///AutoValue
///---------
// If the application doesn&#39;t care about the actual value of the correlation,
// AutoValue type can be used to create a unique correlationId.  An AutoValue
// correlationId behaves exactly the same as any other type of correlation,
// with the exception that it&#39;s value can not be retrieved (but two
// correlationId can be still compared equal).
//..
//  bmqt::CorrelationId corrId = bmqt::CorrelationId::autoValue();
//..
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Correlating Responses
/// - - - - - - - - - - - - - - - -
// Suppose that we have the following asynchronous messaging interface that we
// want to use to implement a basic request/response class.
//..
//  class Messenger {
//
//    public:
//      // TYPES
//      typedef bsl::function&lt;
//        void(void                       *buffer,
//             int                         bufferLength,
//             const bmqt::CorrelationId&amp;  correlationId)&gt; MessageHandler;
//          // &#39;MessageHandler&#39; is an alias for a functor that handles received
//          // messages consisting of the specified &#39;buffer&#39;, having the
//          // specified &#39;bufferLength&#39;, as well as the specified associated
//          // &#39;correlationId&#39; if the message is in response to a previously
//          // transmitted message.
//
//      // MANIPULATORS
//      void sendMessage(void                       *buffer,
//                       int                         bufferLength,
//                       const bmqt::CorrelationId&amp;  correlationId);
//          // Send a message containing the specified &#39;buffer&#39; of the
//          // specified &#39;bufferLength&#39;, associating the specified
//          // &#39;correlationId&#39; with any messages later received in response.
//
//      void setMessageHandler(const MessageHandler&amp; handler);
//          // Set the functor to handle messages received by this object to
//          // the specified &#39;handler&#39;.
//  };
//..
// First we declare a requester class.
//..
//  class Requester {
//
//      // DATA
//      Messenger        *d_messenger_p;  // used to send messages (held)
//      bslma::Allocator *d_allocator_p;  // memory supply (held)
//
//      // PRIVATE CLASS METHODS
//      static void handleMessage(void                       *buffer,
//                                int                         bufferLength,
//                                const bmqt::CorrelationId&amp;  correlationId);
//          // Handle the response message consisting fo the specified &#39;buffer&#39;
//          // having the specified &#39;bufferLength&#39;, and associated
//          // &#39;correlationId&#39;.
//
//    public:
//      // TYPES
//      typedef bsl::function&lt;void(void *buffer, int bufferLength)&gt;
//                                                            ResponseCallback;
//          // &#39;ResponseCallback&#39; is an alias for a functor that is used to
//          // process received responses consisting of the specified &#39;buffer&#39;
//          // having the specified &#39;bufferLength&#39;.
//
//      // CREATORS
//      explicit Requester(Messenger        *messenger,
//                         bslma::Allocator *basicAllocator);
//          // Create a &#39;Requester&#39; that uses the specified &#39;messenger&#39; to send
//          // requests and receive responses, and the specified
//          // &#39;basicAllocator&#39; to supply memory.
//
//      // MANIPULATORS
//      void sendRequest(void                    *buffer,
//                       int                      bufferLength,
//                       const ResponseCallback&amp;  callback);
//          // Send a request consisting of the specified &#39;buffer&#39; having the
//          // specified &#39;bufferLength&#39;, and invoke the specified &#39;callback&#39;
//          // with any asynchronous response.
//  };
//..
// Then, we implement the constructor, setting the message handler on the
// provided &#39;Messenger&#39; to our class method.
//..
//  Requester::Requester(Messenger        *messenger,
//                       bslma::Allocator *basicAllocator)
//  : d_messenger_p(messenger)
//  , d_allocator_p(basicAllocator)
//  {
//      d_messenger_p-&gt;setMessageHandler(&amp;Requester::handleMessage);
//  }
//..
// Now, we implement &#39;sendRequest&#39;, copying the given &#39;callback&#39; into a
// correlationId that is provided to the messenger.
//..
//  void Requester::sendRequest(void                    *buffer,
//                              int                      bufferLength,
//                              const ResponseCallback&amp;  callback)
//  {
//      bsl::shared_ptr&lt;ResponseCallback&gt; callbackCopy;
//      callbackCopy.createInplace(d_allocator_p, callback, d_allocator_p);
//      bmqt::CorrelationId correlationId(bsl::shared_ptr&lt;void&gt;(callbackCopy));
//      d_messenger_p-&gt;sendMessage(buffer, bufferLength, correlationId);
//  }
//..
// Finally, we implement our message handler, extracting the response callback
// from the correlationId, and invoking it with the received response message.
//..
//  void Requester::handleMessage(void                       *buffer,
//                                int                         bufferLength,
//                                const bmqt::CorrelationId&amp;  correlationId)
//  {
//      assert(correlationId.isSharedPtr());
//      ResponseCallback *callback = static_cast&lt;ResponseCallback *&gt;(
//                                         correlationId.theSharedPtr().get());
//      (*callback)(buffer, bufferLength);
//  }
//..
//


// BMQ
#include &lt;bmqscm_version.h&gt;

// BDE
#include &lt;bdlb_variant.h&gt;

#include &lt;bsl_cstdint.h&gt;
#include &lt;bsl_iosfwd.h&gt;
#include &lt;bsl_memory.h&gt;
#include &lt;bsls_assert.h&gt;
#include &lt;bsls_types.h&gt;
#include &lt;bslh_hash.h&gt;

namespace BloombergLP {
namespace bmqt {


                            // ===================
                            // class CorrelationId
                            // ===================

class CorrelationId {
    // This class implements an in-core value-semantic type for correlating an
    // asynchronous result with the original operation.

    // FRIENDS
    //  Needed to access getAsAutoValue
    friend bool operator==(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs);
    friend bool operator!=(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs);
    friend struct CorrelationIdLess;
    friend bsl::ostream&amp; operator&lt;&lt;(bsl::ostream&amp;        stream,
                                    const CorrelationId&amp; rhs);

    template &lt;class HASH_ALGORITHM&gt;
    friend
    void hashAppend(HASH_ALGORITHM&amp; hashAlgo, const CorrelationId&amp; value);

  private:
    // PRIVATE TYPES
    typedef bsls::Types::Uint64 AutoValue;
        // &#39;AutoValue&#39; is an alias for a 64-bit unsigned integer value that is
        // used to generate automatic, opaque, correlationId values.

    // DATA
    bdlb::Variant4&lt;bsls::Types::Int64,
                   void*,
                   bsl::shared_ptr&lt;void&gt;,
                   AutoValue&gt; d_variant;
                               // The variant used to hold the value of a
                               // &#39;CorrelationId&#39;, that may either be unset,
                               // hold a 64-bit integer, a raw pointer, a
                               // shared pointer, or an &#39;AutoValue&#39;.

    // PRIVATE ACCESSORS
    AutoValue theAutoValue() const;
        // Return the value of this correlationId as an &#39;AutoValue&#39;.  The
        // behavior is undefined unless &#39;isAutoValue&#39;.  Note that this method
        // is only available in order to implement the comparison and hash
        // operations.

  public:
    // TYPES
    enum Type {
        e_NUMERIC     // the &#39;CorrelationId&#39; holds a 64-bit integer
      , e_POINTER     // the &#39;CorrelationId&#39; holds a raw pointer
      , e_SHARED_PTR  // the &#39;CorrelationId&#39; holds a shared pointer
      , e_AUTO_VALUE  // the &#39;CorrelationId&#39; holds an auto value
      , e_UNSET       // the &#39;CorrelationId&#39; is not set
    };

    // CLASS METHOD
    static CorrelationId autoValue();
        // Return a &#39;CorrelationId&#39; having a unique, automatically assigned
        // opaque value.

    // CREATORS
    explicit CorrelationId();
        // Create a &#39;CorrelationId&#39; having the default, unset, value.

    explicit CorrelationId(bsls::Types::Int64 numeric);
        // Create a &#39;CorrelationId&#39; object initialized with the specified
        // &#39;numeric&#39; integer value.

    explicit CorrelationId(void *pointer);
        // Create a &#39;CorrelationId&#39; object initialized with the specified
        // &#39;pointer&#39; value.

    explicit CorrelationId(const bsl::shared_ptr&lt;void&gt;&amp; sharedPtr);
        // Create a &#39;CorrelationId&#39; object initialized with the specified
        // &#39;sharedPtr&#39; shared pointer value.

    // MANIPULATORS
    CorrelationId&amp; makeUnset();
        // Unset the value of this object.

    CorrelationId&amp; setNumeric(bsls::Types::Int64 numeric);
        // Set the value of this object value to the specified &#39;numeric&#39;.
        // Return a reference to this object.

    CorrelationId&amp; setPointer(void* pointer);
        // Set the value of this object value to the specified &#39;pointer&#39;.
        // Return a reference to this object.

    CorrelationId&amp; setSharedPointer(const bsl::shared_ptr&lt;void&gt;&amp; sharedPtr);
        // Set the value of this object value to the specified &#39;sharedPtr&#39;.
        // Return a reference to this object.

    // void setAutoValue
        // There is explicitly no setAuto, autoCorrelation should only be used
        // at creation

    // ACCESSORS
    bool isUnset() const;
        // Return &#39;true&#39; if the value of this object is unset;

    bool isNumeric() const;
        // Return &#39;true&#39; if the value of this object is an integer value, and
        // otherwise return &#39;false&#39;.

    bool isPointer() const;
        // Return &#39;true&#39; if the value of this object is a pointer value, and
        // otherwise return &#39;false&#39;.

    bool isSharedPtr() const;
        // Return &#39;true&#39; if the value of this object is a shared pointer value,
        // and otherwise return &#39;false&#39;.

    bool isAutoValue() const;
        // Return &#39;true&#39; if the value of this object is an auto correlation
        // value, and otherwise return &#39;false&#39;.

    bsls::Types::Int64 theNumeric() const;
        // Return the 64-bit integer value of this object.  The behavior is
        // undefined unless method &#39;isNumeric&#39; returns &#39;true&#39;.

    void* thePointer() const;
        // Return the pointer value of this object.  The behavior is undefined
        // unless method &#39;isPointer&#39; returns &#39;true&#39;.

    const bsl::shared_ptr&lt;void&gt;&amp; theSharedPtr() const;
        // Return the pointer value of this object.  The behavior is undefined
        // unless method &#39;isSharedPtr&#39; returns &#39;true&#39;.

    Type type() const;
        // Return the type of the value of this object.

    bsl::ostream&amp; print(bsl::ostream&amp; stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output &#39;stream&#39; at the (absolute
        // value of) the optionally specified indentation &#39;level&#39; and return a
        // reference to &#39;stream&#39;.  If &#39;level&#39; is specified, optionally specify
        // &#39;spacesPerLevel&#39;, the number of spaces per indentation level for
        // this and all of its nested objects.  If &#39;level&#39; is negative,
        // suppress indentation of the first line.  If &#39;spacesPerLevel&#39; is
        // negative format the entire output on one line, suppressing all but
        // the initial indentation (as governed by &#39;level&#39;).  If &#39;stream&#39; is
        // not valid on entry, this operation has no effect.
};

// FREE OPERATORS

                            // -------------------
                            // class CorrelationId
                            // -------------------

bsl::ostream&amp; operator&lt;&lt;(bsl::ostream&amp; stream, const CorrelationId&amp; rhs);
    // Format the specified &#39;rhs&#39; to the specified output &#39;stream&#39; and return
    // a reference to the modifiable &#39;stream&#39;.

bool operator==(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs);
    // Return &#39;true&#39; if &#39;rhs&#39; object contains the value of the same type as
    // contained in &#39;lhs&#39; object and the value itself is the same in both
    // objects, return false otherwise.

bool operator!=(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs);
    // Return &#39;false&#39; if &#39;rhs&#39; object contains the value of the same type as
    // contained in &#39;lhs&#39; object and the value itself is the same in both
    // objects, return &#39;true&#39; otherwise.

bool operator&lt;(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs);
    // Operator used to allow comparison between the specified &#39;lhs&#39; and &#39;rhs&#39;
    // CorrelationId objects so that CorrelationId can be used as key in a map.


                          // =======================
                          // class CorrelationIdLess
                          // =======================

struct CorrelationIdLess
{
    // ACCESSORS
    bool operator()(const CorrelationId&amp; lhs, const CorrelationId&amp; rhs) const;
        // Return &#39;true&#39; if the specified &#39;lhs&#39; should be considered as having
        // a value less than the specified &#39;rhs&#39;.
};


// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================


                            // -------------------
                            // class CorrelationId
                            // -------------------

inline
CorrelationId::AutoValue
CorrelationId::theAutoValue() const
{
    // PRECONDITIONS
    BSLS_ASSERT(isAutoValue());

    return d_variant.the&lt;AutoValue&gt;();
}

// CREATORS
inline
CorrelationId::CorrelationId()
: d_variant()
{
    // NOTHING
}

inline
CorrelationId::CorrelationId(bsls::Types::Int64 numeric)
: d_variant(numeric)
{
    // NOTHING
}

inline
CorrelationId::CorrelationId(void *pointer)
: d_variant(pointer)
{
    // NOTHING
}

inline
CorrelationId::CorrelationId(const bsl::shared_ptr&lt;void&gt;&amp; sharedPtr)
: d_variant(sharedPtr)
{
    // NOTHING
}

// MANIPULATORS
inline
CorrelationId&amp;
CorrelationId::makeUnset()
{
    d_variant.reset();
    return *this;
}

inline
CorrelationId&amp;
CorrelationId::setNumeric(bsls::Types::Int64 numeric)
{
    d_variant.assign(numeric);
    return *this;
}

inline
CorrelationId&amp;
CorrelationId::setPointer(void* pointer)
{
    d_variant.assign(pointer);
    return *this;
}

inline
CorrelationId&amp;
CorrelationId::setSharedPointer(const bsl::shared_ptr&lt;void&gt;&amp; sharedPtr)
{
    d_variant.assign(sharedPtr);
    return *this;
}

// ACCESSORS
inline
bool
CorrelationId::isUnset() const
{
    return d_variant.isUnset();
}

inline
bool
CorrelationId::isNumeric() const
{
    return d_variant.is&lt;bsls::Types::Int64&gt;();
}

inline
bool
CorrelationId::isPointer() const
{
    return d_variant.is&lt;void*&gt;();
}

inline
bool
CorrelationId::isSharedPtr() const
{
    return d_variant.is&lt;bsl::shared_ptr&lt;void&gt; &gt;();
}

inline
bool
CorrelationId::isAutoValue() const
{
    return d_variant.is&lt;AutoValue&gt;();
}

inline
bsls::Types::Int64
CorrelationId::theNumeric() const
{
    // PRECONDITIONS
    BSLS_ASSERT(isNumeric());

    return d_variant.the&lt;bsls::Types::Int64&gt;();
}

inline
void*
CorrelationId::thePointer() const
{
    // PRECONDITIONS
    BSLS_ASSERT(isPointer());

    return d_variant.the&lt;void*&gt;();
}

inline
const bsl::shared_ptr&lt;void&gt;&amp;
CorrelationId::theSharedPtr() const
{
    // PRECONDITIONS
    BSLS_ASSERT(isSharedPtr());

    return d_variant.the&lt;bsl::shared_ptr&lt;void&gt; &gt;();
}

inline
CorrelationId::Type
CorrelationId::type() const
{
    int result = isUnset()     ? e_UNSET
               : isNumeric()   ? e_NUMERIC
               : isPointer()   ? e_POINTER
               : isSharedPtr() ? e_SHARED_PTR
               : isAutoValue() ? e_AUTO_VALUE
               : -1;
    BSLS_ASSERT_OPT(-1 != result &amp;&amp; &quot;Unknown correlation type&quot;);

    return static_cast&lt;Type&gt;(result);
}

// FREE FUNCTIONS
template &lt;class HASH_ALGORITHM&gt;
void
hashAppend(HASH_ALGORITHM&amp;      hashAlgo,
           const CorrelationId&amp; value)
{
    using bslh::hashAppend;  // for ADL

    CorrelationId::Type type = value.type();
    hashAppend(hashAlgo, static_cast&lt;int&gt;(type));

    switch (type) {
      case CorrelationId::e_NUMERIC: {
          hashAppend(hashAlgo, value.theNumeric());
      } break;
      case CorrelationId::e_POINTER: {
          hashAppend(hashAlgo, value.thePointer());
      } break;
      case CorrelationId::e_SHARED_PTR: {
          hashAppend(hashAlgo, value.theSharedPtr());
      } break;
      case CorrelationId::e_AUTO_VALUE: {
          hashAppend(hashAlgo, value.theAutoValue());
      } break;
      case CorrelationId::e_UNSET: {
          hashAppend(hashAlgo, 0);
      } break;
      default: {
        BSLS_ASSERT_OPT(false &amp;&amp; &quot;Unknown correlationId type&quot;);
        hashAppend(hashAlgo, 0);
      }
    }
}

                          // ------------------------
                          // struct CorrelationIdLess
                          // ------------------------

inline
bool
CorrelationIdLess::operator()(const CorrelationId&amp; lhs,
                              const CorrelationId&amp; rhs) const
{
    // If &#39;lhs&#39; and &#39;rhs&#39; have different types, they are compared by their
    // corresponding type indices.  Otherwise, they are compared by value.

    bool result;
    if (lhs.d_variant.typeIndex() != rhs.d_variant.typeIndex()) {
        result = lhs.d_variant.typeIndex() &lt; rhs.d_variant.typeIndex();
    } else if (lhs.isNumeric()) {
        result = lhs.theNumeric() &lt; rhs.theNumeric();
    } else if (lhs.isPointer()) {
        result = reinterpret_cast&lt;bsl::uintptr_t&gt;(lhs.thePointer()) &lt;
                 reinterpret_cast&lt;bsl::uintptr_t&gt;(rhs.thePointer());
    } else if (lhs.isSharedPtr()) {
        result = lhs.theSharedPtr() &lt; rhs.theSharedPtr();
    } else if (lhs.isAutoValue()) {
        result = lhs.theAutoValue() &lt; rhs.theAutoValue();
    } else {
        BSLS_ASSERT_OPT(false &amp;&amp; &quot;Unknown correlator type&quot;);
        result = false;
    }

    return result;
}

}  // close package namespace

                            // -------------------
                            // class CorrelationId
                            // -------------------

// FREE OPERATORS
inline
bool
bmqt::operator==(const bmqt::CorrelationId&amp; lhs,
                 const bmqt::CorrelationId&amp; rhs)
{
    return lhs.d_variant == rhs.d_variant;
}

inline
bool
bmqt::operator!=(const bmqt::CorrelationId&amp; lhs,
                 const bmqt::CorrelationId&amp; rhs)
{
    return lhs.d_variant != rhs.d_variant;
}

inline
bool
bmqt::operator&lt;(const bmqt::CorrelationId&amp; lhs,
                const bmqt::CorrelationId&amp; rhs)
{
    CorrelationIdLess less;
    return less(lhs, rhs);
}

inline
bsl::ostream&amp;
bmqt::operator&lt;&lt;(bsl::ostream&amp;              stream,
                 const bmqt::CorrelationId&amp; rhs)
{
    return rhs.print(stream, 0, -1);
}

}  // close enterprise namespace

#endif
</pre>
</body>
</html>
